home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / Timing / clock1_2.lha / clock1_2 / clock.c < prev    next >
C/C++ Source or Header  |  1994-05-16  |  40KB  |  1,236 lines

  1. /*****************************************************************
  2.  * clock - A flexible, efficient digital clock with date and memory watching
  3.  *
  4.  * Copyright 1994 by Peter Schachte
  5.  * 
  6.  * This is an effecient titlebar digital clock, with date, day of week,
  7.  * free chip/fast/total memory, largest free chip/fast/either memory
  8.  * block, control over formatting, pen color for various parts of the
  9.  * display, and refresh frequency.  The clock can also show the local
  10.  * time in a different time zone.  Clock only updates parts of the
  11.  * display that need updating, so CPU usage is kept low.  This program
  12.  * should work under AmigaOS 1.2 and up.
  13.  *
  14.  * This code is based on Mike Meyer's clock program, though by now
  15.  * very little of his code remains.  In fact, I've changed the
  16.  * spirit of his program, which was quite small and simple; now
  17.  * it's (relatively) large and complex, and probably has altogether
  18.  * too many features.
  19.  *
  20.  * Nevertheless, here's Mike's original copyright notice:
  21.  *================================================================
  22.  * clock - a dumb, digital clock in the upper right-hand corner. Designed
  23.  *    to be small, not flexible!
  24.  *
  25.  * Copyright (c) 1986, Mike Meyer
  26.  *
  27.  * Permission is hereby granted to distribute this program for any purposes
  28.  * whatsoever, so long as this notice, including the above copyright, is
  29.  * included with the distribution. Unlike other people, I don't care if you
  30.  * make money off of this, so long as I get credit for having written it.
  31.  *================================================================
  32.  *
  33.  * I likewise hereby grant the right to distribute this program for
  34.  * any purposes whatsoever, as long as this notice, including my
  35.  * copyright and Mike's, is included with the distribution.
  36.  *
  37.  *****************************************************************/
  38.  
  39. static char version[] =
  40.     "$VER: " PROGNAME " " VERSION " (" __DATE__ ") © 1994 Peter Schachte";
  41.  
  42. /* There is one feature that I have #ifdef'ed out of this program.  If
  43.  * CORRECTION is defined when this file is processed, this clock will
  44.  * support the extra command line argument and tooltype "CORRECTION"
  45.  * which should be set to the number of seconds until your system
  46.  * clock gains or loses a second, positive if it loses a second or
  47.  * negative if it gains a second.  If this option is specified, then
  48.  * the clock will automatically correct the system time as you
  49.  * specify.
  50.  *
  51.  * This feature is only useful if your system clock gains or loses
  52.  * time consistently.  Mine doesn't, and I assume the same is true of
  53.  * others, so I've left this feature out by default.  But I may change
  54.  * my mind and put it back sometime.
  55.  */
  56.  
  57.  
  58. #include "progargs.h"
  59. #include <stdlib.h>
  60. #include <string.h>
  61. #include <exec/types.h>
  62. #include <exec/memory.h>
  63. #include <exec/tasks.h>
  64. #include <devices/timer.h>
  65. #include <libraries/dos.h>
  66. #include <intuition/intuition.h>
  67. #include <intuition/intuitionbase.h>
  68. #include <graphics/rastport.h>
  69. #include <proto/dos.h>
  70. #include <proto/intuition.h>
  71. #include <proto/exec.h>
  72. #include <proto/graphics.h>
  73.  
  74. /* In case we're linked with cback.o */
  75. char *__procname = PROGNAME;
  76. long __BackGroundIO = 0;
  77.  
  78.  
  79.  
  80. /*****************************************************************
  81.                Various Useful Constants
  82. *****************************************************************/
  83.  
  84. #define MINUTES_PER_DAY 1440
  85. #define SECS_PER_DAY 86400
  86. #define END_OF_TIME ((unsigned long) -1)
  87.  
  88. /* (approx?) sizes of workbench gadgets for OS >=2.0 and OS <=1.3 */
  89. #define WIDTH_OF_20_CLOSE_GADGET 20
  90. #define WIDTH_OF_20_DEPTH_GADGET 23
  91. #define WIDTH_OF_13_CLOSE_GADGET 27
  92. #define WIDTH_OF_13_DEPTH_GADGET 27
  93.  
  94. #define INTUITION_REV    1L
  95.  
  96. #define DEFAULT_FORMAT "%2Chip: %1%G%|%2Fast: %1%F%|%e %b %y%|%A%|%q:%M"
  97.  
  98.  
  99. /* An individual part of the display may need any of the following
  100.  * data in order to be displayed.  Some parts may need multiple parts,
  101.  * so we use bit masks to represent them.
  102.  */
  103.  
  104. #define NEED_CHIP    0
  105. #define NEED_FAST    1
  106. #define NEED_BIG_CHIP    2
  107. #define NEED_BIG_FAST    3
  108. #define NEED_TIME    4
  109. #define MAX_NEED    4
  110.  
  111. #define NO_NEEDS_MASK        0
  112. #define NEED_CHIP_MASK        (1<<NEED_CHIP)
  113. #define NEED_FAST_MASK        (1<<NEED_FAST)
  114. #define NEED_MEM_MASK         (NEED_CHIP_MASK|NEED_FAST_MASK)
  115. #define NEED_BIG_CHIP_MASK    (1<<NEED_BIG_CHIP)
  116. #define NEED_BIG_FAST_MASK    (1<<NEED_BIG_FAST)
  117. #define NEED_BIG_MEM_MASK    (NEED_BIG_CHIP_MASK|NEED_BIG_FAST_MASK)
  118. #define NEED_TIME_MASK        (1<<NEED_TIME)
  119.  
  120.  
  121.  
  122. /*****************************************************************
  123.             Maintaining The Window
  124.  
  125. We use a lazy redisplay technology that only paints (about) as much of
  126. the window as needs to be displayed.  To do this, we maintain a chain
  127. of display_element structures, each representing one part of the
  128. display.  In addition, we also maintain two separate chains of the
  129. same structures, one for the time- and date-related fields
  130. (time_chain) and one for the available memory fields (mem_chain).
  131. When we wish to update the display, we traverse the time_chain and
  132. mem_chain, updating only those fields.  When the whole window needs to
  133. be updated, we traverse the whole chain drawing all strings and bars.
  134.  
  135. We are also a bit lazy with respect to updates:  each display_element
  136. contains the value currently displayed in the window, and if we find
  137. that field hasn't changed, we don't do any drawing.  For time/date
  138. fields we take this one step further.  We organize the time_chain in
  139. increasing order of "size" of the units (first seconds, then minutes,
  140. etc.).  When we find an element that hasn't changed, we know that the
  141. following elements won't have changed, either, and so we quit looking.
  142. To make this work, we store the previous value in units since 1 Jan
  143. 1976; e.g., the minute will be stored as minutes since midnight, 1 Jan
  144. 1976.  The minute shown in the clock is just that number modulo 60.
  145.  
  146. One further thing: we want the display to look nice in the presence of
  147. alphabetic month and weekday displays, which vary quite a bit in
  148. length.  Of course, we don't want to resize the window every time the
  149. day changes, and we don't even want the individual segments of the
  150. display, separated by vertical bars, to change in size (the bars
  151. should stay put).  This means that when the day changes, we have to
  152. replan the display.  When we start up, we plan the display assuming
  153. the greatest possible space needed by each field.  Then, each time the
  154. day changes, we replan using the actual sizes of the fields, and
  155. centering each display segment between the (unmoving) bars (or the
  156. ends of the display).
  157.  
  158. *****************************************************************/
  159.  
  160. /* these are the various kinds of data that can be displayed */
  161. enum display_kind {
  162.     invalid, space, vbar, string,
  163.     chip_mem, chip_mem_K, fast_mem, fast_mem_K, total_mem, total_mem_K,
  164.     largest_chip, largest_chip_K, largest_fast, largest_fast_K,
  165.     largest_mem, largest_mem_K,
  166.  
  167.     /* NB:  this group is carefully ordered so that if one of these
  168.      * hasn't changed since the last update, then later ones haven't either
  169.      */
  170.     second, minute, hour12, hour12_0, hour24, hour24_0, am_pm,
  171.  
  172.     /* this group is similarly ordered */
  173.     julian, day, day0, week_num_sun, week_num_mon, weekday_abbrev,
  174.     weekday_full, weekday_num, monthnum0, monthnum, month_abbrev,
  175.     month_full, year, year100
  176. };
  177.  
  178. /* for each part of the display, we have one of these */
  179. struct display_element {
  180.     struct display_element *next;        /* all elements in order */
  181.     struct display_element *next_of_kind;    /* elements of similar kind */
  182.     short left;                    /* pixel left of element */
  183.     short max_width;                /* max width of element */
  184.     short curr_width;                /* current width of elt */
  185.     short pen;                    /* pen to draw in */
  186.     enum display_kind kind;            /* what is to be shown */
  187.     union {
  188.     struct string {                /* for strings: */
  189.         char *text;                /* the string to show */
  190.         int length;                /* char length of string */
  191.     } string;
  192.     struct data {                /* for most other kinds: */
  193.         int curr_value;            /* value currently shown */
  194.     } data;
  195.     };
  196. };
  197.  
  198.  
  199. /*****************************************************************
  200.          Command Line Args and Icon Tooltypes
  201. *****************************************************************/
  202.  
  203. int background_pen = 0;            /* default background is pen 0 */
  204. char *format_string = DEFAULT_FORMAT;
  205. int interval_tenth_secs = 10;        /* default to one update per second */
  206. int offset_minutes = 0;            /* default to no time zone offset */
  207. int priority = 1;            /* default priority */
  208. int win_left;                /* default left:  relative to screen */
  209.                     /* width and window width.  Don't */
  210.                     /* know default till we know OS ver */
  211. int win_top = 0;            /* default top */
  212. int horiz_padding = 5;            /* pixels of padding on either side */
  213.                     /* of clock */
  214. #ifdef CORRECTION
  215. int correction_ratio = 0;        /* add one second to the time */
  216.                     /* every correction_ratio */
  217.                     /* seconds; subtract if negative */
  218. #endif
  219.  
  220. struct arg_descriptor arg_desc[] = {
  221.     INT_ARG("BACKGROUND", background_pen),
  222.     STRING_ARG("FORMAT", format_string),
  223.     INT_ARG("INTERVAL", interval_tenth_secs),
  224.     INT_ARG("OFFSET", offset_minutes),
  225.     INT_ARG("PRIORITY", priority),
  226.     INT_ARG("LEFT", win_left),
  227.     INT_ARG("TOP", win_top),
  228.     INT_ARG("PADDING", horiz_padding),
  229. #ifdef CORRECTION
  230.     INT_ARG("CORRECTION", correction_ratio)
  231. #endif
  232. };
  233.  
  234.  
  235.  
  236. /*****************************************************************
  237.                   Constants
  238. *****************************************************************/
  239.  
  240. static struct NewWindow    New_Window = {
  241.     0, 0, 0, 0,        /* Fill these in later */
  242.     255, 255,        /* Default pens */
  243.     IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW |
  244.     IDCMP_CHANGEWINDOW |
  245.     IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
  246.     WINDOWCLOSE | WINDOWDRAG | NOCAREREFRESH,
  247.     (struct Gadget *) NULL,
  248.     (struct Image *) NULL,
  249.     (UBYTE *) NULL,        /* No title */
  250.     (struct Screen *) NULL,
  251.     (struct BitMap *) NULL,
  252.     0, 0, 0, 0,
  253.     WBENCHSCREEN
  254. };
  255.  
  256. static char *str_am_pm[2] = {"AM", "PM"};
  257. static char *str_weekday[7] = {"Sunday", "Monday", "Tuesday", "Wednesday",
  258.                    "Thursday", "Friday", "Saturday"};
  259. static char *str_month[12] = {"January", "February", "March", "April", "May",
  260.                   "June", "July", "August", "September",
  261.                   "October", "November", "December"};
  262.  
  263. /* I'm using tables for quite a few things here to keep the code
  264.  * small.  It's fairly clean, too, in most places.  In just about all
  265.  * of these cases, there are exceptions and special cases that are
  266.  * treated separately in the code.
  267.  */
  268.  
  269.  
  270. /* display_kind for each upper case format letter */
  271. static char upper_codes[26] = {
  272.       /* A */  (char) weekday_full,
  273.       /* B */  (char) month_full,
  274.       /* C */  (char) invalid, /* short for "%a %b %e %T %Z %Y" */
  275.       /* D */  (char) invalid, /* short for "%m/%d/%y" */
  276.       /* E */  (char) invalid,
  277.       /* F */  (char) fast_mem_K,
  278.       /* G */  (char) chip_mem_K,
  279.       /* H */  (char) hour24_0,
  280.       /* I */  (char) hour12_0,
  281.       /* J */  (char) monthnum,
  282.       /* K */  (char) total_mem,
  283.       /* L */  (char) invalid,
  284.       /* M */  (char) minute,
  285.       /* N */  (char) invalid,
  286.       /* O */  (char) largest_chip_K,
  287.       /* P */  (char) invalid,
  288.       /* Q */  (char) hour12,
  289.       /* R */  (char) invalid, /* short for "%H:%M" */
  290.       /* S */  (char) second,
  291.       /* T */  (char) invalid, /* short for "%H:%M:%S" */
  292.       /* U */  (char) week_num_sun,
  293.       /* V */  (char) largest_fast_K,
  294.       /* W */  (char) week_num_mon,
  295.       /* X */  (char) invalid, /* short for "%H:%M:%S" */
  296.       /* Y */  (char) year,
  297.       /* Z */  (char) largest_mem_K,
  298. };
  299.  
  300. /* display_kind for each lower case format letter */
  301. static char lower_codes[26] = {
  302.       /* a */  (char) weekday_abbrev,
  303.       /* b */  (char) month_abbrev,
  304.       /* c */  (char) invalid, /* short for "%a %b %d %H:%M:%S %Y" */
  305.       /* d */  (char) day0,
  306.       /* e */  (char) day,
  307.       /* f */  (char) fast_mem,
  308.       /* g */  (char) chip_mem,
  309.       /* h */  (char) month_abbrev,
  310.       /* i */  (char) invalid,
  311.       /* j */  (char) julian,
  312.       /* k */  (char) total_mem_K,
  313.       /* l */  (char) invalid,
  314.       /* m */  (char) monthnum0,
  315.       /* n */  (char) invalid, /* ignore newlines */
  316.       /* o */  (char) largest_chip,
  317.       /* p */  (char) am_pm,
  318.       /* q */  (char) hour24,
  319.       /* r */  (char) invalid, /* short for "%I:%M:%S %p" */
  320.       /* s */  (char) invalid,
  321.       /* t */  (char) invalid, /* ignore tabs */
  322.       /* u */  (char) invalid,
  323.       /* v */  (char) largest_fast,
  324.       /* w */  (char) weekday_num,
  325.       /* x */  (char) invalid, /* short for "%m/%d/%y" */
  326.       /* y */  (char) year100,
  327.       /* z */  (char) largest_mem,
  328. };
  329.  
  330.  
  331. static char hms_fmt[] = "%H:%M:%S";
  332.  
  333. /* cases where a format letter is an alias for a longer format */
  334. static char aliases_C[] = "%a %b %e %T %Z %Y";
  335. static char aliases_D[] = "%m/%d/%y";
  336.  
  337. char *aliases_RX[7] = {
  338.       /* R */  "%H:%M",
  339.       /* S */  NULL,
  340.       /* T */  hms_fmt,
  341.       /* U */  NULL,
  342.       /* V */  NULL,
  343.       /* W */  NULL,
  344.       /* X */  hms_fmt
  345. };
  346.  
  347. static char aliases_c[] = "%a %b %d %H:%M:%S %Y";
  348. static char aliases_r[] = "%I:%M:%S %p";
  349. static char aliases_x[] = "%m/%d/%y";
  350.  
  351.  
  352. /* the (max) number of digits in each format kind.  0 means there's a
  353.  * special case for that kind.
  354.  */
  355. char field_digit_width[] = {
  356.     0, /* invalid */
  357.     0, /* space */
  358.     0, /* vbar */
  359.     0, /* string */
  360.     7, /* chip_mem */
  361.     4, /* chip_mem_K */
  362.     8, /* fast_mem */
  363.     5, /* fast_mem_K */
  364.     8, /* total_mem */
  365.     5, /* total_mem_K */
  366.     7, /* largest_chip */
  367.     4, /* largest_chip_K */
  368.     8, /* largest_fast */
  369.     5, /* largest_fast_K */
  370.     8, /* largest_mem */
  371.     5, /* largest_mem_K */
  372.     2, /* second */
  373.     2, /* minute */
  374.     2, /* hour12 */
  375.     2, /* hour12_0 */
  376.     2, /* hour24 */
  377.     2, /* hour24_0 */
  378.     0, /* am_pm */
  379.     3, /* julian */
  380.     2, /* day */
  381.     2, /* day0 */
  382.     2, /* week_num_sun */
  383.     2, /* week_num_mon */
  384.     0, /* weekday_abbrev */
  385.     0, /* weekday_full */
  386.     1, /* weekday_num */
  387.     2, /* monthnum0 */
  388.     2, /* monthnum */
  389.     0, /* month_abbrev */
  390.     0, /* month_full */
  391.     4, /* year */
  392.     2  /* year100 */
  393. };
  394.  
  395.  
  396. /* Which data must be inquired of the OS for each display_kind */
  397. char field_requirements[] = {
  398.     NO_NEEDS_MASK,    /* invalid */
  399.     NO_NEEDS_MASK,    /* space */
  400.     NO_NEEDS_MASK,    /* vbar */
  401.     NO_NEEDS_MASK,    /* string */
  402.     NEED_CHIP_MASK,    /* chip_mem */
  403.     NEED_CHIP_MASK,    /* chip_mem_K */
  404.     NEED_FAST_MASK,    /* fast_mem */
  405.     NEED_FAST_MASK,    /* fast_mem_K */
  406.     NEED_MEM_MASK,    /* total_mem */
  407.     NEED_MEM_MASK,    /* total_mem_K */
  408.     NEED_BIG_CHIP_MASK,    /* largest_chip */
  409.     NEED_BIG_CHIP_MASK,    /* largest_chip_K */
  410.     NEED_BIG_FAST_MASK,    /* largest_fast */
  411.     NEED_BIG_FAST_MASK,    /* largest_fast_K */
  412.     NEED_BIG_MEM_MASK,    /* largest_mem */
  413.     NEED_BIG_MEM_MASK,    /* largest_mem_K */
  414.     NEED_TIME_MASK,    /* second */
  415.     NEED_TIME_MASK,    /* minute */
  416.     NEED_TIME_MASK,    /* hour12 */
  417.     NEED_TIME_MASK,    /* hour12_0 */
  418.     NEED_TIME_MASK,    /* hour24 */
  419.     NEED_TIME_MASK,    /* hour24_0 */
  420.     NEED_TIME_MASK,    /* am_pm */
  421.     NEED_TIME_MASK,    /* julian */
  422.     NEED_TIME_MASK,    /* day */
  423.     NEED_TIME_MASK,    /* day0 */
  424.     NEED_TIME_MASK,    /* week_num_sun */
  425.     NEED_TIME_MASK,    /* week_num_mon */
  426.     NEED_TIME_MASK,    /* weekday_abbrev */
  427.     NEED_TIME_MASK,    /* weekday_full */
  428.     NEED_TIME_MASK,    /* weekday_num */
  429.     NEED_TIME_MASK,    /* monthnum0 */
  430.     NEED_TIME_MASK,    /* monthnum */
  431.     NEED_TIME_MASK,    /* month_abbrev */
  432.     NEED_TIME_MASK,    /* month_full */
  433.     NEED_TIME_MASK,     /* year */
  434.     NEED_TIME_MASK    /* year100 */
  435. };
  436.  
  437.  
  438.  
  439.  
  440.  
  441.  
  442. /*****************************************************************
  443.         Data Shared By Procedures In This File
  444. *****************************************************************/
  445. /*
  446.  * Some things that need to be shared with initialize() and done().
  447.  */
  448. static struct Window        *Window = NULL;
  449. static struct RastPort        *win_rast;
  450. static int            win_baseline;
  451. static int            inleft, intop, inright, inbottom;
  452. static struct timerequest    delay_req;
  453. static struct timerequest    time_req;
  454. static struct MsgPort        *delay_port = NULL;
  455. static struct MsgPort        *time_port = NULL;
  456. static struct Screen        screen_info;
  457. static struct Screen        *my_screen;
  458. static struct RastPort        *scrn_rast;
  459. static int             digit_width; /* pixel width of a digit */
  460. static int            space_width; /* pixel width of space */
  461.  
  462. /* booleans indicating what info we need to query the OS for */
  463. static int            need_time=0, need_chip=0,
  464.                 need_fast=0, need_big_chip=0, need_big_fast=0;
  465.  
  466. static int            new_os;         /* running OS >= 2.0? */
  467. int                 offset_secs; /* computed from offset_minutes */
  468.  
  469. #ifdef CORRECTION
  470. int                correction = 1;    /* secs to add every */
  471.                         /* correction_interval */
  472. #endif
  473.     
  474. static struct display_element    *win_content;   /* all display elements */
  475. /* separate chains (though next_of_kind member) for memory and time displays */
  476. static struct display_element     *mem_chain = NULL;
  477. static struct display_element    *time_chain = NULL;
  478.  
  479.  
  480. struct GfxBase        *GfxBase;
  481. struct IntuitionBase    *IntuitionBase;
  482.  
  483.  
  484.  
  485.  
  486.  
  487. /*****************************************************************
  488.                   Prototypes
  489. *****************************************************************/
  490.  
  491. static void plan_display(char *format, int *pixelpos, int *pen,
  492.              struct display_element ***tailptr);
  493. static void add_display_space(int width, int *pixelpos,
  494.              struct display_element ***tailptr);
  495. static void add_display_string(char *string, int len, int *pixelpos, int *pen,
  496.                    struct display_element ***tailptr);
  497. static void add_display_format(char code, int *pixelpos, int *pen,
  498.                    struct display_element ***tailptr);
  499. static struct display_element *add_element(int width, int *pixelpos, int pen,
  500.                        enum display_kind kind,
  501.                        struct display_element ***tailptr);
  502. static void insert_in_kind(struct display_element *elt,
  503.                struct display_element **chainptr);
  504. static int widest(char **ptr, int count, int charcount);
  505. static void replan_display(int curr_day, int curr_wkday, int curr_month);
  506. static void display_strings(void);
  507. static void draw_bar(long x);
  508. static void display_data(void);
  509. static void draw_datum(struct display_element *ptr, int val,
  510.                char **string_array, int len);
  511. static char *int_string(int n, int *minwidth);
  512. static void initialize(void);
  513. static void done(int code);
  514.  
  515. /* this is defined in compute_date.c: */
  516.  
  517. void compute_date(long n, int *y, int *m, int *d, int *w, int *jul);
  518.  
  519.  
  520.  
  521.  
  522. /*****************************************************************
  523.                  Code
  524. *****************************************************************/
  525.  
  526. void main(int argc, char *argv[]) {
  527.     int             win_width;
  528.  
  529.     struct Layer        *MyLayer;
  530.     struct ClipRect        *MyClip;
  531.     struct IntuiMessage    *Msg;
  532.     struct Task        *FindTask();
  533.     struct TextFont        *font;
  534.     unsigned long        timersig, windowsig, sigmask, sig;
  535.     unsigned long        interval_micros;
  536.     int            pen;
  537.     struct display_element    **tail = &win_content;
  538.  
  539.     /* set up */
  540.     initialize();
  541.     handle_args(argc, argv, arg_desc);
  542.  
  543.     offset_secs = 60 * offset_minutes;
  544.     pen = (background_pen==1 ? 0 : 1);
  545.     inleft = (new_os ? WIDTH_OF_20_CLOSE_GADGET : WIDTH_OF_13_CLOSE_GADGET);
  546.     win_width = inleft + horiz_padding;
  547.     plan_display(format_string, &win_width, &pen, &tail);
  548.     win_width += horiz_padding;
  549.  
  550.     New_Window.LeftEdge = (win_left<0 ?
  551.                    screen_info.Width-win_width+win_left :
  552.                    win_left);
  553.     New_Window.Width = win_width;
  554.     New_Window.Height = screen_info.Font->ta_YSize
  555.       + screen_info.WBorTop + 1;
  556.     New_Window.TopEdge = (win_top<0 ?
  557.                   screen_info.Height-New_Window.Height+win_top :
  558.                   win_top);
  559.  
  560.     Window = (struct Window *) OpenWindow(&New_Window);
  561.     if (Window == NULL) done(20);
  562.     /* set screen title to version string without leading "$VER: " */
  563.     SetWindowTitles(Window, NULL, (UBYTE*) &version[5]);
  564.  
  565.     /* We've opened the window, now store away some info about it */
  566.     win_rast = Window->RPort;
  567.     my_screen = Window->WScreen;
  568.  
  569.     font = OpenFont(screen_info.Font);
  570.     win_baseline = font->tf_Baseline+screen_info.WBorTop;
  571.     SetAPen(win_rast, 1L);
  572.     SetBPen(win_rast, background_pen);
  573.     SetDrMd(win_rast, JAM2);
  574.     SetFont(win_rast, font);
  575.     
  576.     intop = screen_info.WBorTop - 1;
  577.     inright = win_width - 2;
  578.     inbottom = New_Window.Height - 2;
  579.     MyLayer = win_rast->Layer;
  580.  
  581.     timersig = 1L << delay_port->mp_SigBit;
  582.     windowsig = 1L << Window->UserPort->mp_SigBit;
  583.     sigmask = timersig | windowsig | SIGBREAKF_CTRL_C;
  584.     interval_micros = interval_tenth_secs*100000;
  585.  
  586.     {
  587.         int yr, month, dy, weekday, jul;
  588.  
  589.         /* display clock the first time.  First get the current time */
  590.         DoIO((struct IORequest *) &time_req.tr_node);
  591.         compute_date((time_req.tr_time.tv_secs+offset_secs)/SECS_PER_DAY,
  592.              &yr, &month, &dy, &weekday, &jul);
  593.         replan_display(dy, weekday, month);
  594.         display_strings();
  595.         display_data();
  596.     }
  597.  
  598.  
  599.     display_strings();
  600.     display_data();
  601.  
  602.     (void) SetTaskPri(FindTask(NULL), priority);
  603.  
  604. #ifdef CORRECTION
  605.     if (correction_ratio != 0) {
  606.         if (correction_ratio < 0) {
  607.         correction = -1;
  608.         correction_ratio = -correction_ratio;
  609.         }
  610.         need_time = 1;
  611.     }
  612. #endif
  613.  
  614.     for (;;) {
  615.         int strings_need_display = 0;
  616.  
  617.         sig = Wait(sigmask);
  618.         
  619.         if (sig & windowsig) {
  620.         while (Msg=(struct IntuiMessage *)GetMsg(Window->UserPort)) {
  621.             switch (Msg->Class) {
  622.               case IDCMP_CLOSEWINDOW:
  623.             ReplyMsg((struct Message *)Msg);
  624.             done(0);
  625.             break;
  626.               default:
  627.             strings_need_display = 1;
  628.             break;
  629.             }
  630.             ReplyMsg((struct Message *)Msg);
  631.         }
  632.         }
  633.  
  634.         if ((sig & timersig) && GetMsg(delay_port)) {
  635.         delay_req.tr_time.tv_secs = 0;
  636.         delay_req.tr_time.tv_micro = interval_micros;
  637.         SendIO((struct IORequest *) &delay_req.tr_node);
  638.         }
  639.  
  640.         if (sig & SIGBREAKF_CTRL_C) done(20);
  641.  
  642.         /* no point doing anything if our screen is not in front */
  643.         if (my_screen != IntuitionBase->FirstScreen) continue;
  644.  
  645.         /* check if somone is on top of us */
  646.         MyClip = MyLayer->ClipRect;
  647.         if (MyClip && (MyClip->Next || MyClip->lobs)) {
  648.         WindowToFront(Window);    /* keep us on top */
  649.         strings_need_display = 1;
  650.         }
  651.  
  652.         if (strings_need_display) display_strings();
  653.         display_data();
  654.     }
  655.     }
  656.  
  657.  
  658. /* Create a chain of display_elements describing the clock display,
  659.  * and return the interior width of the clock window.  format is the
  660.  * format string.  *pixelpos is the position at which to start the
  661.  * next part of the display; on completion, it is the width of the
  662.  * (inside of) the window.  Pen is the pen color in which to draw the
  663.  * next part of the display.  tailptr points to a pointer to a (NULL)
  664.  * pointer to the next display element, allowing us to keep adding to
  665.  * the end of the chain.  On return, *tailptr will point to the new
  666.  * NULL next pointer.
  667.  */
  668. static void plan_display(char *format, int *pixelpos, int *pen,
  669.              struct display_element ***tailptr)
  670.     {
  671.     char *ptr = format;
  672.  
  673.     do {
  674.         if (*ptr==' ') {
  675.         int width = 0;
  676.  
  677.         do {width += space_width;} while (*++ptr==' ');
  678.         add_display_space(width, pixelpos, tailptr);
  679.         } else if (*ptr == '%') {
  680.         add_display_format(*++ptr, pixelpos, pen, tailptr);
  681.         ++ptr;
  682.         } else if (*ptr != '\0') {
  683.         char *strstart = ptr;
  684.         
  685.         do {++ptr;} while (*ptr!='%' && *ptr!='\0');
  686.         /* we'll handle spaces in next iteration, so trim them */
  687.         while (*--ptr == ' ');        /* trim trailing blanks */
  688.         ++ptr;                /* point after string */
  689.         add_display_string(strstart, ptr-strstart, pixelpos, pen,
  690.                    tailptr);
  691.         }
  692.     } while (*ptr != '\0');
  693.     }
  694.  
  695.  
  696. static void add_display_space(int width, int *pixelpos,
  697.              struct display_element ***tailptr)
  698.     {
  699.     add_element(width, pixelpos, 0, space, tailptr);
  700.     }
  701.  
  702.  
  703. /* create a display_element for the string starting at string and
  704.  * whose length is len.  Add the width of this string to pixelpos.
  705.  */
  706. static void add_display_string(char *str, int len, int *pixelpos, int *pen,
  707.                    struct display_element ***tailptr)
  708.     {
  709.     struct display_element *elt =
  710.       add_element(TextLength(scrn_rast,str,len), pixelpos, *pen, string,
  711.               tailptr);
  712.  
  713.     elt->string.text = str;
  714.     elt->string.length = len;
  715.     }
  716.  
  717.  
  718. /* create a new display_element record or records for format character
  719.  * code.  I.e., we've seen a '%' followed by code, and now want to
  720.  * handle it.
  721.  */
  722. static void add_display_format(char code, int *pixelpos, int *pen,
  723.                    struct display_element ***tailptr)
  724.     {
  725.     struct display_element *elt;
  726.     enum display_kind kind = invalid;
  727.     char *alias = NULL;
  728.     int bits;
  729.     int width;
  730.  
  731.     if (code >= 'A' && code <= 'Z') {
  732.         if (code == 'C') alias = aliases_C;
  733.         else if (code == 'D') alias = aliases_D;
  734.         else if (code >= 'R' && code <= 'X') alias = aliases_RX[code-'R'];
  735.         if (alias != NULL) {
  736.         plan_display(alias, pixelpos, pen, tailptr);
  737.         return;
  738.         } else {
  739.         kind = upper_codes[code-'A'];
  740.         }
  741.     } else if (code >= 'a' && code <= 'z') {
  742.         if (code == 'c') alias = aliases_c;
  743.         else if (code == 'r') alias = aliases_r;
  744.         else if (code == 'x') alias = aliases_x;
  745.         if (alias != NULL) {
  746.         plan_display(alias, pixelpos, pen, tailptr);
  747.         return;
  748.         } else {
  749.         kind = lower_codes[code-'a'];
  750.         }
  751.     } else if (code >= '0' && code <= '9') {
  752.         *pen = code-'0';
  753.         return;
  754.     } else if (code == '|') {
  755.         kind = vbar;
  756.     } else if (code == '%') {
  757.         add_display_string("%", 1, pixelpos, pen, tailptr);
  758.         return;
  759.     }
  760.  
  761.     switch (kind) {
  762.       case weekday_abbrev: width = widest(str_weekday, 7, 3); break;
  763.       case weekday_full:   width = widest(str_weekday, 7, 0); break;
  764.       case month_abbrev:   width = widest(str_month, 12, 3); break;
  765.       case month_full:     width = widest(str_month, 12, 0); break;
  766.       case am_pm:            width = widest(str_am_pm, 2, 0); break;
  767.       case vbar:           width = 2*horiz_padding + 1 + (new_os!=0); break;
  768.       default:           width = digit_width *
  769.                        field_digit_width[(int)kind]; break;
  770.     }
  771.  
  772.     elt = add_element(width, pixelpos, *pen, kind, tailptr);
  773.     elt->data.curr_value = -1;        /* universal bogus value */
  774.  
  775.     bits = field_requirements[(int)kind];
  776.  
  777.     if (bits & NEED_CHIP_MASK) need_chip = 1;
  778.     if (bits & NEED_FAST_MASK) need_fast = 1;
  779.     if (bits & NEED_BIG_CHIP_MASK) need_big_chip = 1;
  780.     if (bits & NEED_BIG_FAST_MASK) need_big_fast = 1;
  781.  
  782.     if (bits & NEED_TIME_MASK) {
  783.         need_time = 1;
  784.         insert_in_kind(elt, &time_chain);
  785.     } else if (bits != NO_NEEDS_MASK) {
  786.         /* must have been one of the memory items */
  787.         insert_in_kind(elt, &mem_chain);
  788.     }
  789.     }
  790.  
  791. /* Create a new generic display_element, fill it in and return it. */
  792. static struct display_element *add_element(int width, int *pixelpos, int pen,
  793.                        enum display_kind kind,
  794.                        struct display_element ***tailptr)
  795.     {
  796.     struct display_element *new = AllocMem(sizeof(struct display_element),
  797.                            MEMF_ANY);
  798.  
  799.     new->next = NULL;
  800.     new->next_of_kind = NULL;
  801.     new->left = *pixelpos;
  802.     new->max_width = width;
  803.     new->curr_width = width;
  804.     *pixelpos += width;
  805.     new->pen = pen;
  806.     new->kind = kind;
  807.     **tailptr = new;
  808.     *tailptr = &(new->next);
  809.     return new;
  810.     }
  811.  
  812.  
  813. /* Insert a display_element into an existing chain (through the
  814.  * next_of_kind field) in sorted order.
  815.  */
  816. static void insert_in_kind(struct display_element *elt,
  817.                struct display_element **chainptr)
  818.     {
  819.     enum display_kind kind = elt->kind;
  820.     struct display_element *ptr = *chainptr;
  821.  
  822.     while (ptr!=NULL && ptr->kind<kind) {
  823.         chainptr = &(ptr->next_of_kind);
  824.         ptr = *chainptr;
  825.     }
  826.     elt->next_of_kind = ptr;
  827.     *chainptr = elt;
  828.     }
  829.  
  830.  
  831. /* Return the width of the widest of the count strings in the array
  832.  * ptr of string pointers.  If charcount is >0, then consider only the
  833.  * first charcount characters of each string.
  834.  */
  835. static int widest(char **ptr, int count, int charcount)
  836.     {
  837.     int max = 0;
  838.     int width;
  839.  
  840.     for (; count>0; --count, ++ptr) {
  841.         width = TextLength(scrn_rast, *ptr, (charcount!=0 ? charcount :
  842.                          strlen(*ptr)));
  843.         if (width > max) max = width;
  844.     }
  845.     return max;
  846.     }
  847.  
  848.  
  849.  
  850. /* Walk down the full display list recentering data in their part of
  851.  * the display.  A part of the display is delimited by vertical bars
  852.  * (or the ends of the window).  For each part, we traverse it once,
  853.  * adding up the actual sizes of everything, and then traverse it
  854.  * again placing everything where it should go.
  855.  */
  856.  
  857. static void replan_display(int curr_day, int curr_wkday, int curr_month)
  858.     {
  859.     struct display_element *ptr = win_content;
  860.     struct display_element *this_part;
  861.     int left = inleft + horiz_padding;
  862.  
  863.     while(ptr != NULL) {
  864.         int changed = 0;            /* has this part changed? */
  865.         int total_width = 0;        /* width of this part */
  866.         int total_max_width = 0;        /* max width of this part */
  867.  
  868.         this_part = ptr;
  869.         for (; ptr!=NULL && ptr->kind!=vbar; ptr=ptr->next) {
  870.         int width;
  871.  
  872.         switch (ptr->kind) {
  873.           case day:
  874.             /* remember:  day is 0 origin, so '9' is the 10th */
  875.             width = digit_width * (curr_day<9 ? 1 : 2);
  876.             break;
  877.           case weekday_abbrev:
  878.             width = TextLength(Window->RPort,str_weekday[curr_wkday],3);
  879.             break;
  880.           case weekday_full:
  881.             width = TextLength(Window->RPort,str_weekday[curr_wkday],
  882.                        strlen(str_weekday[curr_wkday]));
  883.             break;
  884.           case month_abbrev:
  885.             width = TextLength(Window->RPort,str_month[curr_month],3);
  886.             break;
  887.           case month_full:
  888.             width = TextLength(Window->RPort,str_month[curr_month],
  889.                        strlen(str_month[curr_month]));
  890.             break;
  891.           default:
  892.             width = ptr->max_width;    /* width never changes */
  893.             break;
  894.         }
  895.         if (width != ptr->curr_width) {
  896.             ptr->curr_width = width;
  897.             changed = 1;
  898.         }
  899.         total_width += width;
  900.         total_max_width += ptr->max_width;
  901.         }
  902.         if (changed) {
  903.         left += (total_max_width-total_width)/2;
  904.         for (ptr=this_part;
  905.              ptr!=NULL && ptr->kind!=vbar;
  906.              ptr=ptr->next) {
  907.             ptr->left = left;
  908.             left += ptr->curr_width;
  909.         }
  910.         }
  911.         if (ptr != NULL) {
  912.         left = ptr->left + ptr->max_width;
  913.         ptr = ptr->next;
  914.         }
  915.     }
  916.     }
  917.  
  918.  
  919.  
  920.  
  921. /* Clear the window and display all strings and bars.  Also
  922.  * invalidate all previous values for all update records, since after
  923.  * this no values will be up-to-date.
  924.  */
  925. static void display_strings(void)
  926.     {
  927.     struct display_element *ptr;
  928.  
  929.     SetAPen(win_rast, background_pen);
  930.     RectFill(win_rast, inleft, intop, inright, inbottom);
  931.     for (ptr=win_content; ptr!=NULL; ptr=ptr->next) {
  932.         switch(ptr->kind) {
  933.           case invalid: case space:
  934.         break;                /* nothing to do */
  935.           case vbar:
  936.         draw_bar(ptr->left+horiz_padding);
  937.         break;
  938.           case string:
  939.         Move(Window->RPort, ptr->left, win_baseline);
  940.         SetAPen(win_rast, ptr->pen);
  941.         Text(Window->RPort, ptr->string.text, ptr->string.length);
  942.         break;
  943.           default:
  944.         ptr->data.curr_value = -1;    /* force later redisplay */
  945.         }
  946.     }
  947.     }
  948.  
  949.  
  950. /* draw a top-to-bottom bar in window at position x */
  951. static void draw_bar(long x)
  952.     {
  953.     long bottom = Window->BorderTop-2;
  954.     
  955.     if (new_os) {
  956.         /* a nice 3d bar */
  957.         SetAPen(win_rast, 1L);
  958.         Move(Window->RPort, x, 1L);
  959.         Draw(Window->RPort, x, bottom);
  960.         SetAPen(win_rast, 2L);
  961.         Move(Window->RPort, x+1, 1L);
  962.         Draw(Window->RPort, x+1, bottom);
  963.     } else {
  964.         /* boring old single line for pre-2.0 systems*/
  965.         SetAPen(win_rast, 1L);
  966.         Move(Window->RPort, x+1, 1L);
  967.         Draw(Window->RPort, x+1, bottom);
  968.     }
  969.     }
  970.     
  971.  
  972.  
  973.  
  974. /* display all data in clock that have changed since last display. */
  975. static void display_data(void)
  976.     {
  977.     struct display_element *ptr;
  978.     unsigned long secs;
  979.     int chip_free, fast_free, big_chip_free, big_fast_free;
  980.     /* yr < 0 means we haven't computed yr, month, dy, weekday yet */
  981.     int yr=-1, month, dy, weekday, jul;
  982.  
  983.     if (need_time) {
  984. #ifdef CORRECTION
  985.         static unsigned long next_correction = 0;
  986. #endif
  987.         DoIO((struct IORequest *) &time_req.tr_node);
  988.         secs = time_req.tr_time.tv_secs + offset_secs;
  989. #ifdef CORRECTION
  990.         if (secs >= next_correction) {
  991.         if (next_correction == 0) {
  992.             /* need to set up next_correction */
  993.             next_correction = (correction_ratio==0 ?
  994.                        END_OF_TIME :
  995.                        secs + correction_ratio);
  996.         } else {
  997.             /* really need to make a one second correction now */
  998.             time_req.tr_node.io_Command = TR_SETSYSTIME;
  999.             time_req.tr_time.tv_secs += correction;
  1000.             DoIO((struct IORequest *) &time_req.tr_node);
  1001.             time_req.tr_node.io_Command = TR_GETSYSTIME;
  1002.             next_correction += correction_ratio + correction;
  1003.         }
  1004.         }
  1005. #endif
  1006.         for (ptr=time_chain; ptr!=NULL; ptr=ptr->next_of_kind) {
  1007.         int is_date = 0;
  1008.         int modulus;
  1009.         unsigned long val;        /* the numeric value to show */
  1010.         int len = 0;            /* minimum int width, max string
  1011.                          * width (0 means unlimited)
  1012.                          */
  1013.         char **string_array = NULL;    /* the array whose val-th elt
  1014.                          * to show, or NULL, to show
  1015.                          * val as a number
  1016.                          */
  1017.         
  1018.         switch (ptr->kind) {
  1019.           case   hour24:  val=secs/3600; modulus=24;        break;
  1020.           case   hour12:  val=secs/3600; modulus=12;        break;
  1021.           case hour24_0:  val=secs/3600; modulus=24; len=2; break;
  1022.           case hour12_0:  val=secs/3600; modulus=12; len=2; break;
  1023.           case   minute:  val=secs/60;   modulus=60; len=2; break;
  1024.           case   second:  val=secs;      modulus=60; len=2; break;
  1025.           case    am_pm:  val=secs/43200;modulus= 2;
  1026.                   string_array=str_am_pm;           break;
  1027.           default      :  val=secs/SECS_PER_DAY; is_date=1; break;
  1028.         }
  1029.         /* chain is sorted, so when we get to an element that hasn't
  1030.          * changed, we know nothing later will have changed.
  1031.          */
  1032.         if (val == ptr->data.curr_value) break;
  1033.  
  1034.         if (is_date) {
  1035.             if (yr < 0) {
  1036.             /* we haven't figured out the date yet, so do it now. */
  1037.             compute_date(val, &yr, &month, &dy, &weekday, &jul);
  1038.             if (ptr->data.curr_value != -1) {
  1039.                 replan_display(dy, weekday, month);
  1040.                 display_strings();
  1041.                 display_data();
  1042.                 return;
  1043.             }
  1044.             }
  1045.             ptr->data.curr_value = val;
  1046.             switch (ptr->kind) {
  1047.               case           year:  val=yr; break;
  1048.               case        year100:  val=yr%100; len=2; break;
  1049.               case      monthnum0:  val=month+1; len=2; break;
  1050.               case       monthnum:  val=month+1; break;
  1051.               case   month_abbrev:  val=month; len=3;
  1052.                         string_array=str_month; break;
  1053.               case     month_full:  val=month;
  1054.                         string_array=str_month; break;
  1055.               case   week_num_sun:  val=(14+jul-((7+jul-weekday)%7))/7;
  1056.                         break;
  1057.               case   week_num_mon:  val=(14+jul-((8+jul-weekday)%7))/7;
  1058.                         break;
  1059.               case weekday_abbrev:  val=weekday; len=3;
  1060.                         string_array=str_weekday; break;
  1061.               case   weekday_full:  val=weekday;
  1062.                         string_array=str_weekday; break;
  1063.               case    weekday_num:  val=1+weekday; break;
  1064.               case           day0:  val=dy+1; len=2; break;
  1065.               case            day:  val=dy+1; break;
  1066.               case         julian:  val=jul+1; break;
  1067.             }
  1068.         } else {
  1069.             val %= modulus;
  1070.             ptr->data.curr_value = val;
  1071.         }
  1072.         draw_datum(ptr, val, string_array, len);
  1073.         }
  1074.     }
  1075.     if (need_chip) chip_free = AvailMem(MEMF_CHIP);
  1076.     if (need_fast) fast_free = AvailMem(MEMF_FAST);
  1077.     if (need_big_chip) big_chip_free = AvailMem(MEMF_CHIP|MEMF_LARGEST);
  1078.     if (need_big_fast) big_fast_free = AvailMem(MEMF_FAST|MEMF_LARGEST);
  1079.  
  1080.     for (ptr=mem_chain; ptr!=NULL; ptr=ptr->next_of_kind) {
  1081.         int val;                /* the numeric value to show */
  1082.  
  1083.         switch (ptr->kind) {
  1084.           case       chip_mem:  val=chip_free; break;
  1085.           case     chip_mem_K:  val=(chip_free+512)>>10; break;
  1086.           case       fast_mem:  val=fast_free; break;
  1087.           case     fast_mem_K:  val=(fast_free+512)>>10; break;
  1088.           case      total_mem:  val=chip_free+fast_free; break;
  1089.           case    total_mem_K:  val=(chip_free+fast_free+512)>>10; break;
  1090.           case   largest_chip:  val=big_chip_free; break;
  1091.           case largest_chip_K:  val=(big_chip_free+512)>>10; break;
  1092.           case   largest_fast:  val=big_fast_free; break;
  1093.           case largest_fast_K:  val=(big_fast_free+512)>>10; break;
  1094.           case    largest_mem:  val=big_chip_free+big_fast_free; break;
  1095.           case  largest_mem_K:  val=(big_chip_free+big_fast_free+512)>>10;
  1096.                     break;
  1097.         }
  1098.         draw_datum(ptr, val, NULL, 0);
  1099.     }
  1100.     }
  1101.  
  1102.  
  1103. /* display a single datum in the window.  *ptr describes where the
  1104.  * datum should be displayed, val is the value to display.  If
  1105.  * string_array is non-NULL, then show its val-th element as a string,
  1106.  * rather than showing a number.  If len is not 0, show only the first
  1107.  * len characters of the specified string, or show exactly len digits,
  1108.  * 0-filled if necessary, of val if string_array is NULL.
  1109.  */
  1110. static void draw_datum(struct display_element *ptr, int val,
  1111.                char **string_array, int len)
  1112.     {
  1113.     char *todraw;
  1114.     int pixelleft;
  1115.  
  1116.     if (string_array != NULL) {
  1117.         todraw = string_array[val];
  1118.         if (len==0) len=strlen(todraw);
  1119.     } else {
  1120.         todraw = int_string(val, &len);
  1121.     }
  1122.     pixelleft = ptr->left + ptr->curr_width -
  1123.       TextLength(Window->RPort,todraw,len);
  1124.     if (ptr->left < pixelleft) {
  1125.         SetAPen(win_rast, background_pen);
  1126.         RectFill(win_rast, ptr->left, intop, pixelleft-1, inbottom);
  1127.     }
  1128.     Move(Window->RPort, pixelleft, win_baseline);
  1129.     SetAPen(win_rast, ptr->pen);
  1130.     Text(Window->RPort, todraw, len);
  1131.     }
  1132.  
  1133.  
  1134. /* cheap int->string conversion.  Doesn't handle negative numbers, and
  1135.  * always fills to the specified minimum width with 0's.  It returns a
  1136.  * pointer to a static buffer, so one call's value is destroyed by the
  1137.  * next call to this function.  But that's all we need for this clock.
  1138.  * Note that on completion we set *minwidth to the actual width.
  1139.  */
  1140. #define INT_STRING_BUFF_LEN 8
  1141. static char *int_string(int n, int *minwidth)
  1142.     {
  1143.     static char buffer[INT_STRING_BUFF_LEN];
  1144.     char *ptr = &buffer[INT_STRING_BUFF_LEN-1];
  1145.     int len=0, mw=*minwidth;
  1146.  
  1147.     *ptr = '\0';
  1148.     do {
  1149.         *--ptr = '0'+n%10;
  1150.         n/=10;
  1151.     } while ((++len)<mw || n > 0);
  1152.     *minwidth = len;
  1153.     return ptr;
  1154.     }
  1155.  
  1156.  
  1157.  
  1158. /* open libraries and initialize the timer device. */
  1159. static void initialize(void)
  1160.     {
  1161.     if ((IntuitionBase = (struct IntuitionBase *)
  1162.          OpenLibrary("intuition.library", INTUITION_REV)) == NULL)
  1163.       done(20);
  1164.  
  1165.     if ((GfxBase = (struct GfxBase *)
  1166.          OpenLibrary("graphics.library", 0L)) == NULL)
  1167.       done(20);
  1168.  
  1169.     /* set up delay clock, which will wake us every second */
  1170.     if ((delay_port = CreatePort(NULL, 0L)) == NULL)
  1171.       done(20);
  1172.     if (OpenDevice(TIMERNAME, UNIT_VBLANK,
  1173.                (struct IORequest *) &delay_req, 0L) != NULL)
  1174.       done(20);
  1175.     delay_req.tr_node.io_Message.mn_ReplyPort = delay_port;
  1176.     delay_req.tr_node.io_Command = TR_ADDREQUEST;
  1177.     delay_req.tr_node.io_Flags = 0;
  1178.     delay_req.tr_node.io_Error = 0;
  1179.     delay_req.tr_time.tv_secs = 0;
  1180.     delay_req.tr_time.tv_micro = 1000000;
  1181.     /* send first request, to get us started */
  1182.     SendIO((struct IORequest *) &delay_req.tr_node);
  1183.  
  1184.     /* set up time clock, which will get us system time */
  1185.     if ((time_port = CreatePort(NULL, 0L)) == NULL)
  1186.       done(20);
  1187.     if (OpenDevice(TIMERNAME, UNIT_VBLANK /* does this matter? */,
  1188.                (struct IORequest *) &time_req, 0L) != NULL)
  1189.       done(20);
  1190.     time_req.tr_node.io_Message.mn_ReplyPort = time_port;
  1191.     time_req.tr_node.io_Command = TR_GETSYSTIME;
  1192.     time_req.tr_node.io_Flags = 0;
  1193.     time_req.tr_node.io_Error = 0;
  1194.  
  1195.     if (!GetScreenData(&screen_info, sizeof(screen_info), WBENCHSCREEN,
  1196.                NULL))
  1197.       done(20);
  1198.     scrn_rast = &screen_info.RastPort;
  1199.     digit_width = TextLength(scrn_rast, "9", 1);
  1200.     space_width = TextLength(scrn_rast, " ", 1);
  1201.     new_os = ((struct Library *)IntuitionBase)->lib_Version >= 37;
  1202.     /* now we can set the default window left */
  1203.     win_left = -2*(new_os ? WIDTH_OF_20_DEPTH_GADGET
  1204.             : WIDTH_OF_13_DEPTH_GADGET);
  1205.     }    
  1206.  
  1207. /* just clean up that which is open, and then leave. */
  1208. static void done(int code)
  1209.     {
  1210.     handle_args_finish();        /* cleanup arg handling */
  1211.     if (Window) CloseWindow(Window);
  1212.     if (delay_req.tr_node.io_Message.mn_ReplyPort) {
  1213.         AbortIO((struct IORequest *) &delay_req.tr_node);
  1214.         CloseDevice((struct IORequest *) &delay_req);
  1215.     }
  1216.     if (delay_port) DeletePort(delay_port);
  1217.     if (time_req.tr_node.io_Message.mn_ReplyPort)
  1218.         CloseDevice((struct IORequest *) &time_req);
  1219.     if (time_port) DeletePort(time_port);
  1220.     if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
  1221.     if (GfxBase) CloseLibrary((struct Library *)GfxBase);
  1222.  
  1223.     /* free allocated memory */
  1224.     while (win_content != NULL) {
  1225.         struct display_element *next = win_content->next;
  1226.         FreeMem(win_content, sizeof(*win_content));
  1227.         win_content = next;
  1228.     }
  1229.     /* __exit() doesn't try to close files, so it makes a much
  1230.      * smaller executable than using exit().  This is the correct
  1231.      * thing to do, since we compile with the nostdio option.
  1232.      */
  1233.     __exit((long)code);
  1234.     }
  1235.  
  1236.